package cn.demoncat.util.http.entity;

import cn.demoncat.util.lang.MapUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.web.constant.HttpConstant;
import cn.demoncat.util.web.constant.HttpConstant.RequestType;
import cn.demoncat.util.web.entity.HttpParam;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * HTTP 请求对象
 * 
 * @author 延晓磊
 *
 * @since 2019年10月12日
 */
public class HttpRequest extends HttpParam{

	/**
	 * 请求类型：默认null=FORM {@link HttpConstant.RequestType}
	 */
	private RequestType type;
	/**
	 * 请求内容类型（BINARY类型自定义）
	 */
	private String contentType;
	/**
	 * 连接超时时间（秒），0表示使用默认设置
	 */
	private int connectTimeout = 0;
	/**
	 * 读取超时时间（秒），0表示使用默认设置
	 */
	private int readTimeout = 0;
	/**
	 * 流写入超时时间（秒），0表示使用默认设置
	 */
	private int writeTimeout = 0;
	/**
	 * SSL证书
	 */
	private HttpSslCertificateParam certificate;
	/**
	 * 参数：文件上传参数，非空表示Multipart请求
	 */
	private Map<String, List<HttpFileParam>> files;
	/**
	 * 参数：文本(XML/JSON/TEXT)、流(BINARY)
	 */
	private Object body;

	/**
	 * 获取请求（供反序列化用）
	 */
	private HttpRequest() {
		super();
	}

	/**
	 * 获取请求
	 * 
	 * @param url		请求地址
	 * @param method	请求方式
	 */
	private HttpRequest(String url, String method) {
		super(url, method);
	}

	/**
	 * 获取GET请求
	 * 
	 * @param url		请求地址
	 */
	public static HttpRequest get(String url) {
		return new HttpRequest(url, HttpConstant.Method.GET);
	}

	/**
	 * 获取POST请求
	 * 
	 * @param url		请求地址
	 */
	public static HttpRequest post(String url) {
		return new HttpRequest(url, HttpConstant.Method.POST);
	}

	/**
	 * 获取PUT请求
	 *
	 * @param url		请求地址
	 */
	public static HttpRequest put(String url) {
		return new HttpRequest(url, HttpConstant.Method.PUT);
	}

	/**
	 * 获取DELETE请求
	 * 
	 * @param url		请求地址
	 */
	public static HttpRequest delete(String url) {
		return new HttpRequest(url, HttpConstant.Method.DELETE);
	}

	/**
	 * 设置名称
	 *
	 * @param name
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2023年03月29日
	 */
	@Override
	public HttpRequest name(String name){
		super.name(name);
		return this;
	}

	/**
	 * 重试次数
	 *
	 * @param times
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月14日
	 */
	@Override
	public HttpRequest times(Integer times) {
		super.times(times);
		return this;
	}

	/**
	 * 添加请求参数 - 为空时忽略
	 *
	 * @param key		参数名：已存在则替换
	 * @param value		参数值：为空时忽略，非空时trim
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	@Override
	public HttpRequest addParam(String key, String value) {
		super.addParam(key, value);
		return this;
	}

	/**
	 * 添加请求参数 - 为空时忽略
	 *
	 * @param params	参数名:参数值
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	@Override
	public HttpRequest addParam(Map<String, String> params) {
		super.addParam(params);
		return this;
	}

	/**
	 * 添加请求头信息
	 *
	 * @param key		请求头：已存在则替换
	 * @param value		值：为空时忽略，非空时trim
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	@Override
	public HttpRequest addHeader(String key, String value) {
		super.addHeader(key, value);
		return this;
	}

	/**
	 * 添加请求头信息
	 *
	 * @param params	请求头:值
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	@Override
	public HttpRequest addHeader(Map<String, String> params) {
		super.addHeader(params);
		return this;
	}

	/**
	 * 设置文件上传参数：调用后转为Multipart请求
	 * 
	 * @param key		参数名
	 * @param value		参数值，已存在则替换，忽略null值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest addFile(String key, HttpFileParam... value) {
		// 参数为空忽略
		if (StringUtils.isBlank(key) || ArrayUtils.isEmpty(value)) {
			return this;
		}
		// 获取参数集
		if (this.files == null) {
			this.files = new HashMap<>();
		}
		// 覆盖旧参数
		List<HttpFileParam> values = new ArrayList<>();
		this.files.put(key, values);
		// 添加参数值
		for (HttpFileParam val : value) {
			if (val != null) {
				values.add(val);
			}
		}
		// 检查和设置请求类型
		return checkMultipartType();
	}

	/**
	 * 设置文件上传参数：调用后转为Multipart请求
	 * 
	 * @param params	参数名:参数值，已存在则替换，忽略null值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest addFile(Map<String, HttpFileParam> params) {
		// 参数为空忽略
		if (MapUtil.isEmpty(params)) {
			return this;
		}
		// 获取参数集
		if (this.files == null) {
			this.files = new HashMap<>();
		}
		// 遍历设置
		List<HttpFileParam> values;
		for (Entry<String, HttpFileParam> entry : params.entrySet()) {
			if (StringUtils.isNotBlank(entry.getKey())) {
				// 覆盖旧参数
				values = new ArrayList<>();
				this.files.put(entry.getKey(), values);
				// 添加参数值
				if (entry.getValue() != null) {
					values.add(entry.getValue());
				}
			}
		}
		// 检查和设置请求类型
		return checkMultipartType();
	}

	/**
	 * 设置请求体参数：调用后转为JSON文本请求
	 * 
	 * @param body	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest json(String body) {
		return body(StringUtil.toEmpty(body), RequestType.JSON, null);
	}

	/**
	 * 设置请求体参数：调用后转为XML文本请求
	 * 
	 * @param body	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest xml(String body) {
		return body(StringUtil.toEmpty(body), RequestType.XML, null);
	}

	/**
	 * 设置请求体参数：调用后转为TEXT文本请求
	 * 
	 * @param body	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest text(String body) {
		return body(StringUtil.toEmpty(body), RequestType.TEXT, null);
	}

	/**
	 * 设置请求体参数：调用后转为HTML文本请求
	 * 
	 * @param body	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest html(String body) {
		return body(StringUtil.toEmpty(body), RequestType.HTML, null);
	}

	/**
	 * 设置请求体参数：调用后转为二进制流请求
	 * 
	 * @param body
	 * @param contentType	自定义内容类型	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest binary(byte[] body, String contentType) {
		return body(body, RequestType.BINARY, contentType);
	}

	/**
	 * 设置ContentType
	 *
	 * @param contentType
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年02月25日
	 */
	public HttpRequest setContentType(String contentType) {
		this.contentType = contentType;
		return this;
	}

	/**
	 * 设置连接超时时间：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param connectTimeout	连接超时时间（秒），0表示使用默认设置
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest setConnectTimeout(int connectTimeout) {
		if (connectTimeout > 0) {
			this.connectTimeout = connectTimeout;
		}
		return this;
	}

	/**
	 * 设置读取超时时间：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param readTimeout	读取超时时间（秒），0表示使用默认设置
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest setReadTimeout(int readTimeout) {
		if (readTimeout > 0) {
			this.readTimeout = readTimeout;
		}
		return this;
	}

	/**
	 * 设置写入超时时间：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param writeTimeout	写入超时时间（秒），0表示使用默认设置
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest setWriteTimeout(int writeTimeout) {
		if (writeTimeout > 0) {
			this.writeTimeout = writeTimeout;
		}
		return this;
	}

	/**
	 * 设置SSL单向认证：仅用于本次请求，将重新创建OkHttpClient
	 *
	 * 说明：使用默认的证书信任管理器，不做任何校验（相当于其它ssl方法参数为null）
	 *
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest ssl() {
		return sslByTrust(HttpConstant.DEFAULT_TRUST_MANAGER);
	}


	/**
	 * 设置SSL单向认证：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param certs	SSL证书文件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest sslByCerts(InputStream... certs) {
		return sslByPfxCerts(null, null, certs);
	}

	/**
	 * 设置SSL单向认证：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param trustManager	证书管理器
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest sslByTrust(X509TrustManager trustManager) {
		return sslByPfxTrust(null, null, trustManager);
	}

	/**
	 * 设置SSL双向认证：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param pfxStream		客户端证书，支持P12的证书
	 * @param pfxPwd		客户端证书密码
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest sslByPfx(InputStream pfxStream, String pfxPwd) {
		HttpSslCertificateParam cert = new HttpSslCertificateParam();
		cert.setPfxStream(pfxStream);
		cert.setPfxPwd(pfxPwd);
		this.certificate = cert;
		return this;
	}
	
	/**
	 * 设置SSL双向认证：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param pfxStream		客户端证书，支持P12的证书
	 * @param pfxPwd		客户端证书密码
	 * @param certs			含有服务端公钥的证书
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest sslByPfxCerts(InputStream pfxStream, String pfxPwd, InputStream... certs) {
		HttpSslCertificateParam cert = new HttpSslCertificateParam();
		cert.setCertificates(certs);
		cert.setPfxStream(pfxStream);
		cert.setPfxPwd(pfxPwd);
		this.certificate = cert;
		return this;
	}

	/**
	 * 设置SSL双向认证：仅用于本次请求，将重新创建OkHttpClient
	 * 
	 * @param pfxStream		客户端证书，支持P12的证书
	 * @param pfxPwd		客户端证书密码
	 * @param trustManager	证书管理器
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpRequest sslByPfxTrust(InputStream pfxStream, String pfxPwd, X509TrustManager trustManager) {
		HttpSslCertificateParam cert = new HttpSslCertificateParam();
		cert.setTrustManager(trustManager);
		cert.setPfxStream(pfxStream);
		cert.setPfxPwd(pfxPwd);
		this.certificate = cert;
		return this;
	}

	/**
	 * 请求类型：默认null=FORM {@link HttpConstant.RequestType}
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public RequestType getType() {
		if (type == null) {
			type = RequestType.FORM;
		}
		return type;
	}

	/**
	 * 连接超时时间（秒），0表示使用默认设置
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public int getConnectTimeout() {
		return connectTimeout;
	}

	/**
	 * 读取超时时间（秒），0表示使用默认设置
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public int getReadTimeout() {
		return readTimeout;
	}

	/**
	 * 写入超时时间（秒），0表示使用默认设置
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public int getWriteTimeout() {
		return writeTimeout;
	}

	/**
	 * SSL证书
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public HttpSslCertificateParam getCertificate() {
		return certificate;
	}

	/**
	 * 参数：文件上传参数
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public Map<String, List<HttpFileParam>> getFiles() {
		return files;
	}

	/**
	 * 参数：文本(XML/JSON/TEXT)、流(BINARY)
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public Object getBody() {
		return body;
	}

	/**
	 * 请求内容类型（BINARY类型自定义）
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	public String getContentType() {
		if (contentType == null) {
			contentType = RequestType.FORM.getContentType();
		}
		return contentType;
	}

	/**
	 * 检查和设置Multipart请求类型
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	private HttpRequest checkMultipartType() {
		// 设置请求类型
		if (this.type == null) {
			this.type = RequestType.MULTIPART;
			this.contentType = this.type.getContentType();
		}else {
			// 请求类型不能变更
			if (!RequestType.MULTIPART.getCode().equals(this.type.getCode())) {
				throw HttpConstant.REQUEST_TYPE_ERROR;
			}
		}
		return this;
	}
	
	/**
	 * 设置请求体参数
	 * 
	 * @param body			文本(XML/JSON/TEXT)、流(BINARY)
	 * @param type			请求类型
	 * @param contentType	自定义内容类型
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月13日
	 */
	private HttpRequest body(Object body, RequestType type, String contentType) {
		// 请求类型不能变更
		if (this.type != null) {
			throw HttpConstant.REQUEST_TYPE_ERROR;
		}
		// 设置参数
		this.body = body;
		// 设置请求类型
		this.type = type;
		if (contentType == null) {
			if (this.contentType == null) {
				this.contentType = type.getContentType();
			}
		}else{
			this.contentType = contentType;
		}
		return this;
	}
}
